import { any } from "../core/logic";
import { color } from "../core/color";
import { Geometry } from "./Geometry";
import { earcut } from "../core/math/earcut";
/**
 * 拉伸体。
 * @class
 * @memberof w.geometry
 */
class ExtrudeGeometry extends Geometry {
  /**
 * 拉伸体构造方法
 * @constructor
 * @param {*} opts 
 * @param {object} opts.data 原始数据
 * @param {array} opts.data.shape 轮廓position数据
 * @param {array[]} opts.data.holes 孔洞position数据
 * @param {number} opts.data.height 拉伸高度
 * @param {boolean} opts.hasTop 是否有上下封顶，缺省值：true
 * @param {boolean} opts.gradientDirection 渐变方向，可选值，none(无渐变),bt(从下至上渐变),tb(从上至下渐变)，缺省值：none
 * @param {boolean} opts.gradientColor 渐变颜色，缺省值：[1,1,1,1]
 * @param {number} opts.uvRatio uv倍数，缺省值：1
 * @param {boolean} opts.back 【可选值】是否开启后台线程构造拉伸体，缺省值：true，在后台进行解析。
 */
  constructor(opts = {}, share) {
    
    super({autoUpdate: false});
    this.type = "ExtrudeGeometry";
    this._hash = "";
    this.data = any(opts.data, []);
    this.hasTop = any(opts.hasTop, true);
    this.gradientColor = any(color.format(opts.gradientColor), [255,255,255,255]);
    this.gradientDirection = any(opts.gradientDirection, 'none');
    this.closeWall = any(opts.closeWall, true);
    this.sameTop = any(opts.sameTop, false);
    this.uvRatio = any(opts.uvRatio, 1);
    this.map_uvOffset = any(opts.map_uvOffset, [.0,.0]);
    this.init();
  }
  /**
   * @property {Array} map贴图纹理偏移。
   */
  set map_uvOffset(v){
    this._map_uvOffset=v;
    this.addParam("map_uvOffset");
    this.update();
    return this;
  }
  get map_uvOffset(){
    return this._map_uvOffset;
  }
  init() {
    this.verticesArray = [];
    this.colorArray = [];
    this.uvArray = [];
    
    this.data.forEach(item => {
        let bottomPositions = [...item.shape, ...item.holes.flat()];
        let topPositions = [];
        if(this.sameTop) {
            let maxTop = 0;
            for(let i = 0; i < bottomPositions.length; i = i + 3) {
                maxTop = Math.max(bottomPositions[i+2], maxTop);
            }
            for(let i = 0; i < bottomPositions.length; i = i + 3) {
                topPositions.push(bottomPositions[i], bottomPositions[i+1], maxTop + item.height);
            }
        } else {
            for(let i = 0; i < bottomPositions.length; i = i + 3) {
                topPositions.push(bottomPositions[i], bottomPositions[i+1], bottomPositions[i+2] + item.height);
            }
        }
        
        let allPositions = [...bottomPositions, ...topPositions];
        
        let faces = this.getFaces(item.shape, item.holes, bottomPositions);
        if(this.hasTop) {
            this.getBottomSurfaceData(faces, bottomPositions, allPositions);
        }
        this.getSideSurfaceData(item.shape, item.holes, bottomPositions, allPositions);
    })
    this.position = new Float32Array(this.verticesArray)
    this.texCood0 = new Float32Array(this.uvArray)
    this.color = new Uint8Array(this.colorArray)
    this.autoUpdate = true;
    this.update();
    
}
/**
 * 获取所有的面
 * @param {*} shape 
 * @param {*} holes 
 * @param {*} bottomPositions 
 */
getFaces(shape, holes, bottomPositions) {
    let holesStart = [shape.length / 3];
    for(let i = 0 ; i < holes.length-1; i++) {
        holesStart.push(holesStart[i] + holes[i].length/3)
    }
    return earcut(bottomPositions, holes.flat().length ? holesStart : null,3);
}
getBottomSurfaceData(faces, bottomPositions, allPositions) {
    for ( let i = 0; i < faces.length; i=i+3 ) {
        const face = [faces[ i ],faces[ i+1 ],faces[ i+2 ]];
        this.getBottomFaceData( face[ 0 ], face[ 1 ], face[ 2 ], allPositions );
    }
    for ( let i = 0; i < faces.length; i=i+3 ) {
        const face = [faces[ i ],faces[ i+1 ],faces[ i+2 ]];
        this.getBottomFaceData( face[ 2 ] + bottomPositions.length/3 , face[ 1 ] + bottomPositions.length/3, face[ 0 ] + bottomPositions.length/3, allPositions, true );
    }
}
getSideSurfaceData(contour, holes, bottomPositions, allPositions) {
    let layeroffset = 0;
    this.buildSideWall( contour, layeroffset, bottomPositions, allPositions, false);
    layeroffset += contour.length/3;
    for ( let h = 0; h < holes.length; h ++ ) {
        const ahole = holes[ h ];
        this.buildSideWall( ahole, layeroffset, bottomPositions, allPositions, true );
        layeroffset += ahole.length/3;
    }
}
buildSideWall( contour, layeroffset, bottomPositions, allPositions, isHole ) {
    let i = contour.length/3;
    let flag = 0;
    if(!isHole && !this.closeWall) {
        flag = 1;
    }
    while ( -- i >= flag ) {
        const j = i;
        let k = i - 1;
        if ( k < 0 ) k = contour.length/3 - 1;
        const a = layeroffset + j ,
            b = layeroffset + k ,
            c = layeroffset + k + bottomPositions.length/3,
            d = layeroffset + j + bottomPositions.length/3;
        this.getSideFaceData( a, b, c, d, allPositions );
    }
}
getBottomFaceData( a, b, c, allPositions, isTop) {
    this.addVertex( a, allPositions, isTop );
    this.addVertex( b, allPositions, isTop );
    this.addVertex( c, allPositions, isTop );
    const nextIndex = this.verticesArray.length / 3;
    const uvs = this.getBottomUV(this.verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
    this.addUV( uvs[ 0 ] );
    this.addUV( uvs[ 1 ] );
    this.addUV( uvs[ 2 ] );

}
getSideFaceData( a, b, c, d, allPositions ) {
    this.addVertex( a, allPositions );
    this.addVertex( b, allPositions );
    this.addVertex( d , allPositions, true);
    this.addVertex( b, allPositions );
    this.addVertex( c, allPositions, true );
    this.addVertex( d, allPositions, true );
    const nextIndex = this.verticesArray.length / 3;
    const uvs = this.getSideUV( this.verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
    this.addUV( uvs[ 0 ] );
    this.addUV( uvs[ 1 ] );
    this.addUV( uvs[ 3 ] );
    this.addUV( uvs[ 1 ] );
    this.addUV( uvs[ 2 ] );
    this.addUV( uvs[ 3 ] );
}
addVertex( index, allPositions, isTop  ) {
    this.verticesArray.push( allPositions[ index * 3 + 0 ] );
    this.verticesArray.push( allPositions[ index * 3 + 1 ] );
    this.verticesArray.push( allPositions[ index * 3 + 2 ] );
    let [r,g,b,a] = this.gradientColor;
    if(this.gradientDirection === 'bt') {
        if(isTop) {
            this.colorArray.push(r,g,b,0)
        } else {
            this.colorArray.push(r,g,b,a)
        }
    } else if (this.gradientDirection === 'tb') {
        if(isTop) {
            this.colorArray.push(r,g,b,a)
        } else {
            this.colorArray.push(r,g,b,0)
        }
    } else {
        this.colorArray.push(r,g,b,a)
    }
    
}
addUV( uv ) {
    this.uvArray.push( uv[0] * this.uvRatio );
    this.uvArray.push( uv[1] * this.uvRatio );
}
getBottomUV( vertices, indexA, indexB, indexC ) {
    const a_x = vertices[ indexA * 3 ];
    const a_y = vertices[ indexA * 3 + 1 ];
    const b_x = vertices[ indexB * 3 ];
    const b_y = vertices[ indexB * 3 + 1 ];
    const c_x = vertices[ indexC * 3 ];
    const c_y = vertices[ indexC * 3 + 1 ];
    return [
        [a_x, a_y],
        [b_x, b_y],
        [c_x, c_y]
    ];
}
getSideUV( vertices, indexA, indexB, indexC, indexD ) {
    const a_x = vertices[ indexA * 3 ];
    const a_y = vertices[ indexA * 3 + 1 ];
    const a_z = vertices[ indexA * 3 + 2 ];
    const b_x = vertices[ indexB * 3 ];
    const b_y = vertices[ indexB * 3 + 1 ];
    const b_z = vertices[ indexB * 3 + 2 ];
    const c_x = vertices[ indexC * 3 ];
    const c_y = vertices[ indexC * 3 + 1 ];
    const c_z = vertices[ indexC * 3 + 2 ];
    const d_x = vertices[ indexD * 3 ];
    const d_y = vertices[ indexD * 3 + 1 ];
    const d_z = vertices[ indexD * 3 + 2 ];
    if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) {
        return [
            [a_x, 1 - a_z],
            [b_x, 1 - b_z],
            [c_x, 1 - c_z],
            [d_x, 1 - d_z]
        ];
    } else {
        return [
            [a_y, 1 - a_z],
            [b_y, 1 - b_z],
            [c_y, 1 - c_z],
            [d_y, 1 - d_z]
        ];
    }
}
  toGPU() {
    if (this.has("map_uvOffset")) {
        let data = this.map_uvOffset;
        this.bufferToGPU(new Float32Array(data), 'map_uvOffset');
      }
    super.toGPU();
  }
    

  
  
}

export { ExtrudeGeometry };
